背包加密分为加法背包和乘法背包。
1、加法背包:我们知道,1<2,1+2<4,1+2+4<8,1+2+4+8<16,……,那么如果我们选择这样一些数,这些数从小到大排列,如果前面所有的数加起来的值总小于后面的数,那么这些数就可以构成一个背包,我们给一个这个背包里面的某些数的和,这个数就是被加密的数,由这个背包组成这个数只有一种组合方式,这个方式就是秘密了,例如给大家一个封包(2,3,6,12,24,48),由这个背包里的某些数构成的数:86,你知道86怎么来的吗?当然,你看着背包里面的内容,可以知道是由2+12+24+48得到的,如果你没有这个背包,而是直接得到这个86,你知道组成这个86的最小的数是多少吗?你无法知道,因为加起来等于86的数非常多:85+1=86,84+2=86等等,你是无法知道的,所以,背包加密非常难破。
2、乘法背包:乘法背包比加法背包更复杂,不仅是运算量大了很多,更重要的是你得到的一个被加密了的数据更大,一般都是上亿的,而且在许多机密的机关里面,背包的数据都不是有这个单位,而是用位。我们知道,1<2,1*2<3,1*2*3<7,1*2*3*7<43,1*2*3*7*42<1765,数字的增长还是很快的,之所以复杂,就是因为数字很大啊!背包的特点是:如果背包里面的数据按小到大排列,那么,前面所有数据的乘积小于后面的任何一个元素,这个就是背包的特点,是不是很简单,但是要知道乘积的数字的增长是非常快的!
应用领域:
很多数据都需要加密,例如银行的数据、网络游戏、军事机构、行政机构以及其他重要场所。不过虽然这种加密效果非常好,但是加密的程度太大也不现实,一般不会有非常复杂的加密,如果一个数据就几百位,而且还用非常规进制,那么可以想象电脑要算多久啊,会多么影响速度啊!
公钥算法之背包算法
这是一个非对称算法,即可生成多个不同的公钥,分发给其他人,然后其他人用各自的公钥加密文件,而算法只生成一个私钥(自己保存),这私钥可解密不同公钥加密的文件。在不知道私钥的前提下,破解文件是一个NP难问题。
下面贴上高老师的讲义:
附上自己的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
/*该程序加解密文本长度最多为16个二进制位(包括16字节),即2字节*/
#include <stdio.h>
#define N 30
bool
text[N],detext[N];
//储存待加密文本和解密文本
int
n,pubkey[N];
//公钥β
int
w[N];
//超级增长序列
typedef
struct
Tri
//三元组结构体
{
int
d,x,y;
}Tri;
Tri Extend_Euclid(
int
a,
int
b)
//扩展欧几里德算法
{
Tri t1,t2;
if
(b==0)
{
t1.d=a,t1.x=1,t1.y=0;
//(a,1,0)
return
t1;
}
//(d,x,y)←(d',y',x'-a/b*y')
t2=Extend_Euclid(b,a%b);
t1.d=t2.d;
t1.x=t2.y;
t1.y=t2.x-a/b*t2.y;
return
t1;
}
void
Create_Super_Growth()
//创建超级增长序列
{
int
i,sum=0;
for
(w[0]=1,i=1;i<N;i++)
sum+=w[i-1]+1,w[i]=sum;
return
;
}
int
Encode(
int
q,
int
r)
//加密
{
int
i,c=0;
printf
(
"公钥序列:\n"
);
for
(i=0;i<n;i++)
//计算公钥β
{
pubkey[i]=w[i]*r%q;
printf
(
"%d: %d\n"
,i,pubkey[i]);
}
for
(i=0;i<n;i++)
//加密文本
c=(c+text[i]*pubkey[i])%q;
return
c;
}
void
Decode(
int
q,
int
r,
int
c)
//解密
{
int
i;
__int64
k,s;
//防止k=c*s%q结果溢出
Tri t;
for
(i=0;i<n;i++) detext[i]=0;
t=Extend_Euclid(r,q);
//扩展欧几里德求r逆元
s=t.x;
while
(s<0) s+=q;
//把负逆元转换为正逆元
printf
(
"r逆元:%d\n"
,s);
//开始解密
k=s*c%q;
printf
(
"k=%d\n"
,k);
for
(i=n-1;i>=0;i--)
//贪心策略解子集合问题,解密出二进制文本
if
(k>=w[i]) k-=w[i],detext[i]++;
return
;
}
int
main()
{
//随机选择q和r,q>w序列1到n范围内元素之和,q和r要互素
int
i,c,q=0,r;
//r为乘数,q为模数
Tri t;
Create_Super_Growth();
//创建超级增长序列
for
(i=0;i<16;i++)
//假设q=w[0]+...+w[15]+1,即加密长度不超过16byte
q+=w[i];
q++;
for
(i=2;;i++)
//寻找第一个与q互数r(不一定要第一个)
{
t=Extend_Euclid(q,i);
if
(t.d==1) {r=i;
break
;}
}
// freopen("公钥算法之背包算法.txt","r",stdin);
while
(
scanf
(
"%d"
,&n)!=EOF)
{
for
(i=0;i<n;i++)
scanf
(
"%d"
,&text[i]);
//二进制待加密文本
c=Encode(q,r);
//加密,得到密文
printf
(
"密文:%d\n"
,c);
Decode(q,r,c);
//解密
printf
(
"解密后文本:\n"
);
for
(i=0;i<n;i++)
printf
(
"%d "
,detext[i]);
printf
(
"\n"
);
}
return
0;
}
|
样例输入:
16 1 0 0 1 1 0 1 0 1 1 1 0 0 1 1 0
样例输出:
公钥序列:
0: 3
1: 6
2: 15
3: 33
4: 69
5: 141
6: 285
7: 573
8: 1149
9: 2301
10: 4605
11: 9213
12: 18429
13: 36861
14: 73725
15: 49165
密文:20743
r逆元:32763
k=39677
解密后文本:
1 0 0 1 1 0 1 0 1 1 1 0 0 1 1 0